package com.lin.filegraph.utils.extractor;

import com.lin.filegraph.utils.filedg.*;
import com.lin.filegraph.utils.parser.node.*;
import com.lin.filegraph.utils.parser.simpleast.SimpleASTHelper;

import java.util.*;

/**
 * @description: 依赖关系抽取
 * @author: linhuaixu
 * @time: 2023/5/18 11:07
 */
public class DependencyExtractor {
    private String projectPath = null;
    private JProjectNode root;

    public DependencyExtractor(String projectPath, JProjectNode root) {
        // TODO Auto-generated constructor stub
        this.projectPath = projectPath;
        this.root = root;
    }

    public void calculateDependencyOfFiles() {
        FileGraph fdg = root.getFdg();
        List<JFileNode> allFiles = SimpleASTHelper.findAllFilesInSubTree(root);
        Map<String , Set<String>> associations = new HashMap<>();
        for (JFileNode fileNode1 : allFiles) {
            for (JFileNode fileNode2 : allFiles) {
                if (fileNode1.equals(fileNode2)) {
                    continue;
                }
                if (hasFileDependency(fileNode1, fileNode2)) {
                    CouplingInfo couplingInfo = analyzeCoupling(fileNode1, fileNode2, associations);
                    int couplingElements = couplingInfo.getNumberOfCouplingElements();
                    int allElements = couplingInfo.getNumerOfElements();

                    // numOfDependencyElements > 0,或存在继承关系，说明两文件之间存在边
                    if (couplingInfo.hasCoupling()) {
                        FileGraphNode fgNode1 = fdg.getFileGraphNode(fileNode1.getFilePath());
                        FileGraphNode fgNode2 = fdg.getFileGraphNode(fileNode2.getFilePath());
                        if (!fdg.getPostFiles(fgNode1).contains(fgNode2)) {
                            FileGraphEdge newEdge = new FileGraphEdge(fgNode1, fgNode2, couplingInfo, false);
                            fdg.addEdge(newEdge);
                        }
                    }
                }
            }
        }
        //只有A关联依赖域于B，则A组合依赖于B
        for(Map.Entry<String, Set<String>> entry: associations.entrySet()) {
            if(entry.getValue().size() == 1) {
                String file2 = entry.getKey();
                for(String file1 : entry.getValue()) {
                    FileGraphEdge edge = fdg.getFileGraphEdge(file1, file2);
                    edge.addCouplingType(CouplingType.Combination);
                }
            }
        }
    }

    private boolean hasFileDependency(JFileNode fileNode1, JFileNode fileNode2) {
        JPackageNode package1 = fileNode1.getPackage();
        JPackageNode package2 = fileNode2.getPackage();
        if (package1.equals(package2)) {
            return true;
        }

        for (JImportNode importNode : fileNode1.getAllImports()) {
            if (importNode.getImportPackageName().equals(package2.getPackageName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 计算fileNode1依赖于fileNode2的元素个数
     *
     * @param fileNode1
     *            依赖文件
     * @param fileNode2
     *            被依赖文件
     * @param associations
     * @return fileNode1依赖于fileNode2的元素个数
     */
    private CouplingInfo analyzeCoupling(JFileNode fileNode1, JFileNode fileNode2, Map<String , Set<String>> associations) {
//		Map<CouplingType, Integer> mapOfCouplingCounter = new HashMap<>();
        Set<JTreeNode> couplingElements = new LinkedHashSet<>();
        CouplingInfo couplingInfo = new CouplingInfo();
        List<String> allTypeNamesOfFile2 = fileNode2.getAllTypeNames();
        List<JTypeNode> allTypesOfFile1 = SimpleASTHelper.findAllTypesInSubTree(fileNode1);
        List<JFieldNode> allFieldsOfFile1 = SimpleASTHelper.findAllFieldsInSubTree(fileNode1);
        List<JMethodNode> allMethodsOfFile1 = SimpleASTHelper.findAllMethodsInSubTree(fileNode1);
        int allElements = allTypesOfFile1.size() + allFieldsOfFile1.size() + allMethodsOfFile1.size();
        couplingInfo.setNumerOfElements(allElements);
        // for (TypeNode typeNodeOfFile1 : allTypesOfFile1) {
        // allFieldsOfFile1.addAll(typeNodeOfFile1.getAllFields());
        // allMethodsOfFile1.addAll(typeNodeOfFile1.getAllMethods());
        // }

        for (JTypeNode type1 : allTypesOfFile1) {
            for (JTypeNode type2 : fileNode2.getAllTypes()) {
                // 接口依赖
                if (type1.getListStrSuperInterface().contains(type2.getTypeName())) {
                    couplingInfo.addCouplingCount(CouplingType.Implements);
                    couplingElements.add(type1);
                }
                // 继承依赖
                if (type1 instanceof JClassNode) {
                    JClassNode class1 = (JClassNode) type1;
                    if (type2.getTypeName().equals(class1.getStrSuperClass())) {
                        couplingInfo.addCouplingCount(CouplingType.Inheritance);
                        couplingElements.add(type1);
                    }
                }
            }
        }
        for (JFieldNode fieldNodeOfFile1 : allFieldsOfFile1) {
            // 组合依赖
            if (allTypeNamesOfFile2.contains(fieldNodeOfFile1.getReturnType())) {
                couplingInfo.addCouplingCount(CouplingType.Other_Association);
                couplingElements.add(fieldNodeOfFile1);
                if(associations.get(fileNode2.getFilePath()) == null) {
                    associations.put(fileNode2.getFilePath(), new LinkedHashSet<>());
                }
                associations.get(fileNode2.getFilePath()).add(fileNode1.getFilePath());
            }
        }
        for (JMethodNode methodNodeOfFile1 : allMethodsOfFile1) {
            // 参数依赖
            List<JParameterNode> parameters = methodNodeOfFile1.getAllParameters();
            for (JParameterNode parameter : parameters) {
                if (allTypeNamesOfFile2.contains(parameter.getParameterType())) {
                    couplingInfo.addCouplingCount(CouplingType.Parameter);
                    couplingElements.add(methodNodeOfFile1);
                }
            }

            // 返回值依赖
            if (allTypeNamesOfFile2.contains(methodNodeOfFile1.getReturnType())) {
                couplingInfo.addCouplingCount(CouplingType.Return);
                couplingElements.add(methodNodeOfFile1);
            }


            List<JMethodInvocationNode> listOfMethodInvocations = methodNodeOfFile1.getListMethodInvokeNode();
            for (JMethodInvocationNode methodInvocation : listOfMethodInvocations) {
                if (allTypeNamesOfFile2.contains(methodInvocation.getStrDeclaringType())) {
                    if(methodInvocation.getDeclaringMethod() == null) {
                        continue;
                    }
                    if (methodInvocation.getDeclaringMethod().getMethodClassification()
                            .equals(JMethodNode.MethodClassification.isConstructor)) {
                        // 实例化依赖
                        couplingInfo.addCouplingCount(CouplingType.Instantiation);
                    } else {
                        // 方法调用依赖
                        couplingInfo.addCouplingCount(CouplingType.Call);
                    }
                    couplingElements.add(methodNodeOfFile1);
                }
            }

            // 变量声明依赖
            List<String> listOfQuoteTypes = methodNodeOfFile1.getListOfQuoteTypes();
            for (String quoteType : listOfQuoteTypes) {
                if (allTypeNamesOfFile2.contains(quoteType)) {
                    couplingInfo.addCouplingCount(CouplingType.DeclarationType);
                    couplingElements.add(methodNodeOfFile1);
                }
            }

        }
        couplingInfo.setNumberOfCouplingElements(couplingElements.size());
        return couplingInfo;
    }
}

